﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace fes
{

  #region FuzzySet

  public class FuzzySet
  {
    public enum FuzzySetType {LeftTrapeze, Triangle, RightTrapeze};
    public String name;
    private FuzzySetType type;

    public FuzzySetType T
    {
      get { return type; }
      set { type = value; }
    }
    public FuzzySetType EnumType { get { return type; } }
    public String Type 
    {
        get 
        {
            switch (type) 
            {
                case FuzzySetType.LeftTrapeze: return "Lewy Trapez";
                case FuzzySetType.RightTrapeze: return "Prawy Trapez";
                case FuzzySetType.Triangle: return "Trójkąt";
                default: return "Nieznany";
            }
        } 
    }
    private double[] hedge;
    public double[] Hedge { get { return hedge; } set { hedge = value; } }
    public double crispValue = 0;
    public double fuzzyValue = 0;
    
    public FuzzySet(double[] h, String n, FuzzySetType t)
    {
      hedge = h;
      name = n;
      type = t;
    }

    public double getFuzzyValue(double cv)
    {
      double retval = 0;

      switch (type)
      {
        case FuzzySetType.LeftTrapeze:
          if (cv < hedge[0])
          {
            retval = 1.0;
          }
          else if (cv >= hedge[0] && cv < hedge[1])
          {
            retval = (hedge[1] - cv) / (hedge[1] - hedge[0]);
          }
          else
          {
            retval = 0.0;
          }
          break;
        case FuzzySetType.Triangle:
          if (cv <= hedge[0] || cv >= hedge[2])
          {
            retval = 0.0;
          }
          else if (cv > hedge[0] && cv <= hedge[1])
          {
            retval = (cv - hedge[0]) / (hedge[1] - hedge[0]);
          }
          else if (cv > hedge[1] && cv < hedge[2])
          {
            retval = (hedge[2] - cv) / (hedge[2] - hedge[1]);
          }
          break;
        case FuzzySetType.RightTrapeze:
          if (cv < hedge[0])
          {
            retval = 0.0;
          }
          else if (cv >= hedge[0] && cv < hedge[1])
          {
            retval = (cv - hedge[0]) / (hedge[1] - hedge[0]);
          }
          else
          {
            retval = 1.0;
          }
          break;
        default:
          return 0;
      }
      return retval;
    }

    public void fuzzify(double c)
    {
      crispValue = c;
      fuzzyValue = getFuzzyValue(c);
    }
  }

  #endregion 


  #region FuzzyValue

  public class FuzzyValue
  {
    public List<FuzzySet> fuzzySets;
    public String name;

    public FuzzyValue(String n)
    {
      name = n;
      fuzzySets = new List<FuzzySet>();
    }

    public void addFuzzySet(double[] h, String n, FuzzySet.FuzzySetType t)
    {
      FuzzySet fs = new FuzzySet(h, n, t);
      fuzzySets.Add(fs);
    }

    public void fuzzify(double cv)
    {
      int i = 0;
      for (i = 0; i < fuzzySets.Count; ++i)
      {
        fuzzySets[i].fuzzify(cv);
      }
    }

    public FuzzySet getFuzzySet(String n)
    {
      for (int i = 0; i < fuzzySets.Count; ++i)
      {
        if (n == fuzzySets[i].name)
        {
          return fuzzySets[i];
        }
      }
      return new FuzzySet(null, "", FuzzySet.FuzzySetType.RightTrapeze);
    }

  }

#endregion


  #region FuzzyRule

  class FuzzyRule
  {
    public String antecedent1;
    public String antecedent2;
    public String consequent;
    
    public FuzzyRule(String a1, String a2, String c)
    {
      antecedent1 = a1;
      antecedent2 = a2;
      consequent = c;
    }
  }
  
  #endregion


  #region FuzzyAssociativeMemory

  public class FuzzyAssociativeMemory
  {
    private FuzzyValue solution = new FuzzyValue("test");
    private FuzzyValue input1;
    private FuzzyValue input2;
    private FuzzyRule[] fr;
    private double[] ruleVals = new double[9];
    private double[] bmHedge = new double[2] {0.150, 0.325};
    private double[] bfHedge = new double[3] {0.150, 0.325, 0.500};
    private double[] dtHedge = new double[3] {0.325, 0.500, 0.675};
    private double[] sfHedge = new double[3] {0.500, 0.675, 0.850};
    private double[] smHedge = new double[2] {0.675, 0.850};

    public FuzzyAssociativeMemory(FuzzyValue fv1, FuzzyValue fv2)
    {
      solution = new FuzzyValue("solution");
      solution.addFuzzySet(bmHedge, "BM", FuzzySet.FuzzySetType.LeftTrapeze);
      solution.addFuzzySet(bfHedge, "BF", FuzzySet.FuzzySetType.Triangle);
      solution.addFuzzySet(dtHedge, "DT", FuzzySet.FuzzySetType.Triangle);
      solution.addFuzzySet(sfHedge, "SF", FuzzySet.FuzzySetType.Triangle);
      solution.addFuzzySet(smHedge, "SM", FuzzySet.FuzzySetType.RightTrapeze);
//TODO: wyciagnal konfiguracje do formularza
      fr = new FuzzyRule[9];
      fr[0] = new FuzzyRule("L","L","BF");
      fr[1] = new FuzzyRule("L","M","BF");
      fr[2] = new FuzzyRule("L","H","BM");
      fr[3] = new FuzzyRule("M","L","SF");
      fr[4] = new FuzzyRule("M","M","DT");
      fr[5] = new FuzzyRule("M","H","BF");
      fr[6] = new FuzzyRule("H","L","SM");
      fr[7] = new FuzzyRule("H","M","SF");
      fr[8] = new FuzzyRule("H","H","SF");
      
      input1 = fv1;
      input2 = fv2;
    }

      public void FuzzyAssociativeMemoryInputs(FuzzyValue fv1, FuzzyValue fv2)
      {
         input1 = fv1;
         input2 = fv2;
      }

    public void reason()
    {
      double a1 = 0;
      double a2 = 0;
      double uRule = 0;
      double uSet = 0;

      //evaluate each rule
      for (int i = 0; i < 9; ++i)
      {
        a1 = input1.getFuzzySet(fr[i].antecedent1).fuzzyValue;
        a2 = input2.getFuzzySet(fr[i].antecedent2).fuzzyValue;
        ruleVals[i] = min(a1, a2);
      }

      //aggregate output sets into solution region
      for (int i = 0; i < 9; ++i)
      {
        uRule = ruleVals[i];
        uSet = solution.getFuzzySet(fr[i].consequent).fuzzyValue;

        if (uRule > uSet)
        {
          solution.getFuzzySet(fr[i].consequent).fuzzyValue = uRule;
        }
      }
    }
    public double defuzzify()
    {
      double sum = 0;
      double wSum = 0;
      double maxFuzzy = 0;
      double fuzzyVal = 0;
      double capped = 0;
      double curve = 0;

      for (int i = 0; i <= 100; ++i)
      {
        maxFuzzy = 0;
        for (int j = 0; j < solution.fuzzySets.Count; ++j)
        {
          curve = solution.fuzzySets[j].getFuzzyValue(i / (double) 100);
          capped = solution.fuzzySets[j].fuzzyValue;
          fuzzyVal = min(curve, capped);
          if (fuzzyVal > maxFuzzy)
          {
            maxFuzzy = fuzzyVal;
          }
        }

        sum += maxFuzzy;
        wSum += maxFuzzy * (i / (double) 100);
      }

      if (sum > 0)
      {
        return wSum / sum;
      }

      return 0;
    }

    private double min(double x, double y)
    {
      return (x < y) ? x : y;
    }
  }
}

  #endregion